/*
Copyright (c) 2009 Ralf Baumbach
sshortcut@ralfbaumbach.com

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
var tmpFile;
var gInfoButton, gDoneButton, gConnectButton;
var bookmarks;

function setup() {
  
	// init shiny buttons
	gInfoButton = new AppleInfoButton(document.getElementById("infoButton"), document.getElementById("front"), "black", "black", showPrefs);
	gDoneButton = new AppleGlassButton(document.getElementById("doneButton"), "Done", hidePrefs);
	gConnectButton = new AppleGlassButton(document.getElementById("connectButton"), "Connect", connectSSH);
	gResetButton = new AppleGlassButton(document.getElementById("resetButton"), "Reset", reset);
	gSaveBookmarkButton = new AppleGlassButton(document.getElementById("saveBookmarkButton"), "s", updateBookmark);
	gAddBookmarkButton = new AppleGlassButton(document.getElementById("addBookmarkButton"), "+", addBookmark);
	gRemoveBookmarkButton = new AppleGlassButton(document.getElementById("removeBookmarkButton"), "-", removeBookmark);
	
	// init bookmarks select
	initBookmarks();
	
	// init the rest
	initPreferences();
	updateUI();
	document.getElementById('configuration-footer-link').onclick = function() {
		widget.openURL('http://sshortcut.ralfbaumbach.de');
	};
}

// init bookmarks select
function initBookmarks() {
	bookmarks = new Bookmarks(getPref('bookmarks', ''));
	updateBookmarkSelect();
	
	document.getElementById('bookmarks').onchange = function() {

		if (this.value != -1) 	{ setUIValues(bookmarks.getObj(this.value)); }
		else 					          { setUIValues(null); }
		
		// call the keyup event to reset colors in case the fields have been marked as errors before
		document.getElementById('account').onkeyup();
		document.getElementById('server').onkeyup();
	};
}

// update the user interface
function updateUI() {
	//Toggle the display of the keychain add box when passwd is empty(helps to make its functionality more obvious)
	document.getElementById('keychainCheckbox').style.display = (document.getElementById('password').value != '' && getPref('useKeychain')) ? "block" : "none";
}


function setUIValues(obj) {
	if (obj !== null) {
		document.getElementById('account').value = obj.account;
		document.getElementById('server').value = obj.server;
		document.getElementById('port').value = obj.port;
		document.getElementById('options').value = obj.options;
	} else {
		document.getElementById('account').value = '';
		document.getElementById('server').value = '';
		document.getElementById('port').value = '';
		document.getElementById('options').value = '';
	}
}

// make a SSH connection
function connectSSH() {
	account  = document.getElementById('account').value;
	server   = document.getElementById('server').value;
	port     = document.getElementById('port').value;
	password = document.getElementById('password').value;
	options  = document.getElementById('options').value;
	addToKeychain = document.getElementById('addToKeychain').checked;
	
	alreadyInKeychain = false;
	var error = false;
	
	// if nothing given, do nothing, but give visual feedback
	if (account == '') {
	  markErrorFields(document.getElementById('account'));
		error = true;
	}
	
	if (server == '') {
	  markErrorFields(document.getElementById('server'));
		error = true;
	} else {
	  if (server.indexOf('.') == -1) {
	    markErrorFields(document.getElementById('server'));
      error = true;
	  } else if (!verifyNetworkAccess(server)) {
	    // verify that the server is accessible
      markErrorFields(document.getElementById('server'));
		  error = true;
		}
	}
	
	// return if error
	if (error === true) {
	  return false;
	}
	
	// collect ssh arguments
	arguments = '';
	
	// check wether we will accept RSA certificates automatically or not
	arguments += ' ' + getPref('acceptRSA') ? "1" : "0";
	
	if (port != '') {
		arguments += ' -p ' + port + '';
	}
	
	// add additional options
	arguments += ' ' + options;
	
	// check if we should try to use password from keychain
	if (getPref('useKeychain') && password == '') {
		command = '/usr/bin/security find-internet-password';
		command+= ' -ga ' + account;
		command+= ' -s ' + server;
		command+= ' -P ' + port;
		keychainObject = widget.system(command, null);
		
		// if we get a positive response from keychain we will set the password
		if (keychainObject.status == 0) {
			password = keychainObject.errorString
			password = password.replace(/"/g, '');
			password = password.replace(/\n/g, '');
			password = password.replace('password: ', '');
			alreadyInKeychain = true;
		}
	}
	
	// if we have a password we will use expect to communicate with the ssh connection
	if (password != '') {
		// construct the command for executing the expect script
		tmpFile = getTempName();
		user = widget.system('/usr/bin/whoami', null).outputString.replace(/\n/, '');
		tmp = "#!/bin/sh\n";
		tmp+= "/Users/" + user + "/Library/Widgets/sshortcut.wdgt/sshortcut " + account + ' ' + server + ' ' + password + ' ' + arguments;
		tmp = tmp.replace(/'/g, "\\\'");
		tmp = tmp.replace(/"/g, '\\\"');
		status = widget.system('/bin/echo "' + tmp + '" > ' + tmpFile + '\n chmod +x ' + tmpFile, null);
		command = tmpFile;
	} else {
		// otherwise we will just attempt a normal connection and the user has to enter the passsword manually.
		//command = "/usr/bin/ssh " + account + ' ' + server + ' ' + arguments;
		command = "/usr/bin/ssh " + arguments + ' ' + account + '@' + server;
	}
	
	// let's do it
	result = widget.system(makeTerminalCommand(command), null);
	window.setTimeout("removeTempFile()", 1000);
	
	// shall we add a used password to the keychain?
	if (getPref('useKeychain') && addToKeychain && !alreadyInKeychain && password != '') {
		command = '/usr/bin/security add-internet-password';
		command+= ' -a ' + account;
		command+= ' -s ' + server;
		command+= ' -P ' + port;
		command+= ' -r ssh';
		command+= ' -w ' + password;
		keychainObject = widget.system(command, null);
	}
	
	return false;
}




// reset the input fields
function reset() {
	document.getElementById('account').value = '';
	document.getElementById('server').value = '';
	document.getElementById('port').value = '';
	document.getElementById('password').value = '';
	document.getElementById('options').value = '';
	document.getElementById('bookmarks').value = -1;
}

// checks if network is accessible (using ping)
// there is probably a better way to do this...And here it is!!
function verifyNetworkAccess(server) {
	command = '/sbin/ifconfig | grep -q "status: active"';
	result = widget.system(command, null);
	return result.status == 0;
}

// remove the temporary created file
function removeTempFile()
{
	widget.system('/bin/rm ' + tmpFile, null);
}

// create a temporary file as argument for the expect command
function getTempName()
{
	var date = new Date();
	var tmpDir = '/tmp';
	if (tmpDir[0] != '/') tmpDir = '/' + tmpDir;
	if (tmpDir[tmpDir.length - 1] != '/') tmpDir += '/';
	return tmpDir + date.getTime() + '.sshortcut';
}

function makeTerminalCommand(command)
{
	var terminalCommand = 'osascript';
	terminalCommand+= ' -e "tell application \\"Terminal\\" to do script \\"' + command + '\\""';
	terminalCommand+= ' -e "activate application \\"Terminal\\""';
	terminalCommand+= ' -e "tell application \\"System Events\\" to key code 111"';
	return terminalCommand;
}

function addBookmark() {
	account  = document.getElementById('account').value;
	server   = document.getElementById('server').value;
	port     = document.getElementById('port').value;
	options  = document.getElementById('options').value;
	index = bookmarks.add(account, server, port, options);
	setPref('bookmarks', bookmarks.getString());
	updateBookmarkSelect(index);
}

function removeBookmark() {
	bookmarks.remove(document.getElementById('bookmarks').value);
	setPref('bookmarks', bookmarks.getString());
	updateBookmarkSelect();
	setUIValues(null);
}

function updateBookmark() {
	account = document.getElementById('account').value;
	server  = document.getElementById('server').value;
	port    = document.getElementById('port').value;
	options = document.getElementById('options').value;
	index   = document.getElementById('bookmarks').value;
	bookmarks.update(index, account, server, port, options);
	setPref('bookmarks', bookmarks.getString());
	updateBookmarkSelect(index);
}

function updateBookmarkSelect(index) {
	document.getElementById('bookmarks').innerHTML = '<option value="-1">Bookmarks</option>';
	document.getElementById('bookmarks').innerHTML += bookmarks.toOptions();
	if (typeof index != 'undefined') {
		document.getElementById('bookmarks').value = index;
	}
}

// retrieve a preference
function getPref(key, defaultValue)
{
	value = widget.preferenceForKey(key);
	
	if (!value && typeof defaultValue != 'undefined')
		return defaultValue;
	if (value == 'true')
		return true;
	if (value == 'false')
		return false;
	return value;
}

// set a preference
function setPref(key, value)
{
	if (value === true)
		widget.setPreferenceForKey('true', key);
	else if (value === false)
		widget.setPreferenceForKey('false', key);
	else
		widget.setPreferenceForKey(value, key);
}

// init the options on the configuration pane
function initPreferences()
{
	document.getElementById('useKeychain').checked = getPref('useKeychain', true);
	document.getElementById('acceptRSA').checked = getPref('acceptRSA', false);
}

// save preferences and update user interface
function savePreferences()
{
	setPref('useKeychain', document.getElementById('useKeychain').checked);
	setPref('acceptRSA', document.getElementById('acceptRSA').checked);
	updateUI();
}

// show preference pane
function showPrefs()
{
	var front = document.getElementById("front");
	var back = document.getElementById("back");
 
	if (window.widget) {
		widget.prepareForTransition("ToBack");
 	}
	
	front.style.display="none";
	back.style.display="block";
	
	if (window.widget){
		setTimeout ('widget.performTransition();', 0);
		widget.setCloseBoxOffset(13,14);
	}
}

// hide preference pane
function hidePrefs()
{
	var front = document.getElementById("front");
	var back = document.getElementById("back");
	
	if (window.widget) {
		widget.prepareForTransition("ToFront");
 	}

	back.style.display="none";
	front.style.display="block";
	
	if (window.widget) {
		setTimeout ('widget.performTransition();', 0);
		widget.setCloseBoxOffset(7,9);
	}
}

function markErrorFields(obj) {
	setColor(obj, 'red');
	obj.onkeyup = toggleColor;
	obj.onchange = toggleColor;
}

function toggleColor() {
	if (this.style.color == 'red') {
		setColor(this, '');
		this.removeEventListener('keyup', toggleColor, false);
		this.removeEventListener('change', toggleColor, false);
	}
}

function setColor(obj, color) {
	obj.style.color = color;
	obj.style.borderColor = color;
}


// bookmark object
Bookmarks = function(bookmarkString) {
	
	var me = this;
	
	me.list = new Array();
	me.string = bookmarkString;
	
	// get initial bookmark string
	if (me.string != '')
		me.list = me.string.split(';');
	
	me.getString = function() {
		me.string = me.list.join(';');
		return me.string;
	};
	
	me.makeString = function(obj) {
		return obj.account + '@' + obj.server + ':' + obj.port + '#' + obj.options;
	};
	
	me.getObjFromString = function(str) {
    
		tmp = str.split('@');
		
		account = tmp[0];
		tmp = tmp[1].split(':');
		
		server = tmp[0];
		tmp = tmp[1].split('#');
		
		port = tmp[0];
		options = tmp[1];
		
		return me.makeObj(account, server, port, options);
	};
	
	me.makeObj = function(account, server, port, options) {
		return {'account': account, 'server': server, 'port': port, 'options': options};
	};
	
	me.has = function(account, server, port, options) {
		tmp = me.string + ';';
		return tmp.indexOf(me.makeString(me.makeObj(account, server, port, options)) + ';') != -1;
	};
	
	me.add = function(account, server, port, options) {
		if (account == '' || server == '') return false;
		if (me.has(account, server, port, options)) return me.getIndex(me.makeObj(account, server, port, options));
		index = me.list.length;
		me.list[index] = me.makeString(me.makeObj(account, server, port, options));
		return index;
	};
	
	me.update = function(index, account, server, port, options) {
		if (account == '' || server == '') return false;
		me.list[index] = me.makeString(me.makeObj(account, server, port, options));
		return true;
	};
	
	me.remove = function(i) {
		newlist = new Array();
		count = 0;
		for (j = 0; j < me.list.length; j++) {
			if (j != i) {
				newlist[count] = me.list[j];
				count++;
			}
		}
		me.list = newlist;
	};
	
	me.getIndex = function(obj) {
		for (i = 0; i < me.list.length; i++) {
			listItem = me.list[i];
			if (listItem == me.makeString(obj)) { return i; }
		}
		return -1;
	};
	
	me.getObj = function(i) {
		return me.getObjFromString(me.list[i])
	};
	
	me.toOptions = function() {
		options = '';

		if (me.list.length == 0 )
			return;
    
		me.sort();
		
		for (i = 0; i < me.list.length; i++) {
			options += '<option value=' + i + '>' + me.list[i] + '</option>';
		}
		return options;
	};
	
	me.length = function() {
		return me.list.length;
	};
	
	me.sort = function() {
		sorting = true;
		objList = new Array();
		
		for (i = 0; i < me.list.length; i++) {
			objList[i] = me.getObjFromString(me.list[i]);
		}
		
		objList.sort(me.sortCallbackFunc);
		
		for (i = 0; i < objList.length; i++) {
			me.list[i] = me.makeString(objList[i]);
		}
	};
	
	me.sortCallbackFunc = function(a,b){

		if (a.server == b.server) {
			if(a.account == b.account)
				return 0;

			return (a.account < b.account) ? -1 : 1;
		}
		
		return (a.server < b.server) ? -1 : 1;
	};
}
